package movee.manager.ftp;

import movee.domain.constants.FileSyncErrType;
import movee.utils.CloseUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * ftp客户端
 *
 * @author movee
 */
@Slf4j
public class FtpClient {

    private final FTPClient client;
    private final String host;
    private final int port;

    public FtpClient(String host, int port, String userName, String password) throws IOException {
        this.host = host;
        this.port = port;

        client = new FTPClient();
        client.setDefaultTimeout(30 * 1000);
        client.setConnectTimeout(30 * 1000);
        client.setDataTimeout(300 * 1000);

        client.connect(host, port);
        client.login(userName, password);
        client.setControlEncoding("UTF-8");
        client.setFileType(FTPClient.BINARY_FILE_TYPE);
        client.enterLocalPassiveMode();

        client.setReceiveBufferSize(1024 * 1024);
        client.setBufferSize(1024 * 1024);
    }

    public FTPFile[] listFiles(String path) throws IOException {
        return client.listFiles(path);
    }

    public Long getRemoteFileSize(String filePath) throws IOException {
        FTPFile[] ftpFiles = client.listFiles(filePath);
        if (ftpFiles == null || ftpFiles.length == 0) {
            log.debug("file is not exist on ftpserver. filePath: {}", filePath);
            return null;
        } else {
            return ftpFiles[0].getSize();
        }
    }

    public boolean upload(String localPath, String remotePath) throws IOException {

        boolean res = false;
        FileInputStream fis= new FileInputStream(localPath);
        int index= remotePath.lastIndexOf("/");

        int  filenameplace= localPath.lastIndexOf(".");

        String filename= localPath.substring(localPath.lastIndexOf("\\") + 1, filenameplace);

        String fileType= localPath.substring(filenameplace);

        if (-1 != index) {
            client.setFileType(FTPClient.BINARY_FILE_TYPE);

            client.changeWorkingDirectory(remotePath.substring(0, index));

            res = client.storeFile(filename + fileType, fis);
        }
        fis.close();
        return res;
    }

    public FileSyncErrType downLoad(String remotePath, String localPath) throws IOException {

        String dir = localPath.substring(0, localPath.lastIndexOf('/'));
        File dirFile = new File(dir);
        if (!dirFile.exists()) {
            boolean success = dirFile.mkdirs();
            if (success) {
                success = dirFile.setReadable(true, false);
                if (!success) {
                    log.warn("set readable permission failed. dir: {}", dir);
                }
                success = dirFile.setWritable(true, false);
                if (!success) {
                    log.warn("set writable permission failed. dir: {}", dir);
                }
                success = dirFile.setExecutable(true, false);
                if (!success) {
                    log.warn("set executable permission failed. dir: {}", dir);
                }
            } else {
                log.warn("create directory failed. dir: {}", dir);
            }
        }

        // 服务器上文件大小，不存在则返回false
        Long remoteFileSize = getRemoteFileSize(remotePath);
        if (remoteFileSize == null) {
            return FileSyncErrType.FILE_NOT_EXIST;
        } else if (remoteFileSize <= 0) {
            return FileSyncErrType.FILE_LEN_ZERO;
        }

        InputStream remoteFileInputStream = null;
        ByteArrayOutputStream localByteOutputStream = null;
        FileOutputStream localFileOutputStream = null;
        try {
            remoteFileInputStream = client.retrieveFileStream(remotePath);
            if (remoteFileInputStream != null) {
                localByteOutputStream = new ByteArrayOutputStream(1024 * 1024);
                byte[] buffer = new byte[100 * 1024];
                int bytes = 0;
                do {
                    bytes = remoteFileInputStream.read(buffer);
                    if (bytes > 0) {
                        localByteOutputStream.write(buffer, 0, bytes);
                    }
                } while (bytes > 0);

                long receivedBytes = localByteOutputStream.size();
                File file = new File(localPath);
                if (receivedBytes >= remoteFileSize || receivedBytes > file.length()) {
                    localFileOutputStream = new FileOutputStream(localPath);
                    localFileOutputStream.write(localByteOutputStream.toByteArray());
                }

                return receivedBytes >= remoteFileSize ?
                        FileSyncErrType.OK : FileSyncErrType.DOWNLOAD_NOT_FULL;
            }
        } finally {
            if (remoteFileInputStream != null) {
                remoteFileInputStream.close();
                client.completePendingCommand();
            }

            CloseUtils.close(localByteOutputStream);
            CloseUtils.close(localFileOutputStream);
        }

        return FileSyncErrType.CONNECT_FAIL;
    }

    public boolean delete(String remotePath) throws IOException {

        return  client.deleteFile(remotePath) || deleteDirectory(remotePath);
    }


    public boolean makeDirectory(String remotePath) throws IOException {

        String[] item = remotePath.split("/");
        String currentPath = "";
        for (int i = 0; i < item.length - 1; i++) {

            currentPath = currentPath + "/" + item[i];
            client.makeDirectory(currentPath);
        }
        return client.makeDirectory(remotePath);
    }

    public boolean deleteDirectory(String remotePath) throws IOException {
        FTPFile[] files = listFiles(remotePath);
        if (files == null) {
            return true;
        }
        log.debug("========to delete remotePath. file or sub directory count: {}. remotePath: {}",
                files.length, remotePath);
        for (FTPFile ftpFile : files) {
            if (ftpFile.isDirectory()) {
                // 是目录，递归删除
                deleteDirectory(remotePath + File.separator + ftpFile.getName());
            } else {
                // 是文件，直接删除
                client.deleteFile(remotePath + File.separator + ftpFile.getName());
            }
        }

        // 目录已为空，直接删除
        return client.removeDirectory(remotePath);
    }

    public boolean rename(String remoteOldPath, String remoteNewPath) throws IOException {
        return client.rename(remoteOldPath, remoteNewPath);
    }

    public void close() throws IOException {
        if (null != client) {
            boolean success = client.logout();
            if (!success) {
                log.error("logout failed. ftpServer: {}:{}", host, port);
            }
            client.disconnect();
        }
    }

}
